// Projeto desenvolvido para trabalhar com imagem no formato .bmp de 32 bits o valor do pixel
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <time.h>
#include <omp.h>

//Prototipo das funcoes
uint32_t soma_grupo(uint32_t **a, int32_t m, int32_t n, int pos_m, int pos_n);

int main(int argc, char *argv[]){
    
    FILE *fp,*fp1;                     // Ponteiro do arquivo de imagem
    unsigned char vet_antes[18];       // Vetor que carrega o cabecalho antes daos valores width e height 
    unsigned char vet_depois[28];      // Vetor que carrega o cabecalho depois daos valores width e height e vai ate chegar antes do primeiro pixel RGBA
    int i,j;
    
    int32_t width;                     // Largura da imagem 
    int32_t height;                    // Altura da imagem 
    
    uint32_t **red;                    // Componente vermelha
    uint32_t **green;                  // Componente verde
    uint32_t **blue;                   // Componente azul
    
    uint32_t **smooth_red;             // Componente vermelha apos aplicar smooth
    uint32_t **smooth_green;           // Componente verde apos aplicar smooth
    uint32_t **smooth_blue;            // Componente azul apos aplicar smooth
    
    //Variaveis para medir tempo de execucao
    clock_t t0, tf;

    //Inicializa o cronometro
    t0 = clock();
    
    //---------- Lendo imagem como arquivo binario ---------------------------------
    
    fp = fopen(argv[1],"r+b");
    
    if(fp == NULL)
       printf("Erro ao abrir o arquivo...\n");
    
    else{// se o arquivo existir
       
    rewind(fp);
    
    //lendo o cabecalho ate chegar no valor width e heigth
    for(i=0;i<18;i++)
       fread(&vet_antes[i],sizeof(unsigned char),1,fp);  
    
    //identificando os valores da largura e alturda da imagem  
    fread(&width,sizeof(unsigned char),4,fp);  
    fread(&height,sizeof(unsigned char),4,fp);  
    printf("\n%d Largura da imagem\n%d Altura da imagem\n",width,height);
    
    //lendo o cabecalho depois dos valores width e heigth até chegar no primeiro pixel RGB
    for(i=0;i<28;i++)
       fread(&vet_depois[i],1,1,fp);  
       
    // ----------------- alocando as matrizes RGBA -----------------------------
    
    // aloca as linhas da matriz Red
    red = (uint32_t **) malloc (height * sizeof(uint32_t *));
    // aloca as colunas da matriz Red
    for ( i = 0; i < height; i++ )
       red[i] = (uint32_t*) malloc (width* sizeof(uint32_t)); 
       
    // aloca as linhas da matriz Green
    green = (uint32_t **) malloc (height * sizeof(uint32_t *));
    // aloca as colunas da matriz Green
    for ( i = 0; i < height; i++ )
       green[i] = (uint32_t*) malloc (width* sizeof(uint32_t));
       
    // aloca as linhas da matriz blue
    blue = (uint32_t **) malloc (height * sizeof(uint32_t *));
    // aloca as colunas da matriz blue
    for ( i = 0; i < height; i++ )
       blue[i] = (uint32_t*) malloc (width* sizeof(uint32_t));          
    
    //Lendo os pixels RGB   
    for(i=0;i<height;i++){
       for(j=0;j<width;j++){
                            
           red[i][j] = 0;
           green[i][j] = 0;
           blue[i][j] = 0;
           
           fread(&red[i][j],1,1,fp);  
           fread(&green[i][j],1,1,fp);
           fread(&blue[i][j],1,1,fp);
       } 
    }
    
    // ------- APLICANDO SMOOTH -----------------------------------------------
    
    // alocando as matrizes RGBA smooth
    
    // aloca as linhas da matriz Red
    smooth_red = (uint32_t **) malloc (height * sizeof(uint32_t *));
    // aloca as colunas da matriz Red
    for ( i = 0; i < height; i++ )
       smooth_red[i] = (uint32_t*) malloc (width* sizeof(uint32_t)); 
       
    // aloca as linhas da matriz Green
    smooth_green = (uint32_t **) malloc (height * sizeof(uint32_t *));
    // aloca as colunas da matriz Green
    for ( i = 0; i < height; i++ )
       smooth_green[i] = (uint32_t*) malloc (width* sizeof(uint32_t));
       
    // aloca as linhas da matriz blue
    smooth_blue = (uint32_t **) malloc (height * sizeof(uint32_t *));
    // aloca as colunas da matriz blue
    for ( i = 0; i < height; i++ )
       smooth_blue[i] = (uint32_t*) malloc (width* sizeof(uint32_t)); 
    
    //aplicando o smooth para cada pixel de cada matriz 

    // --------- USO DA OPENMP -------------------------------------------------
    omp_set_num_threads(3); // Setando o numero  de threads
    int aux;
    #pragma omp parallel for private(i,j) shared(smooth_red,smooth_green,smooth_blue)
    for(i=0;i<aux;i++){
        for(j=0;j<width;j++){
	    
            aux = omp_get_thread_num();
            if(aux == 0)
		smooth_red[i][j] = soma_grupo(red,height,width,i,j); 
            else if(aux == 1)
    		smooth_green[i][j] = soma_grupo(green,height,width,i,j);
            else if(aux == 2)
		smooth_blue[i][j] = soma_grupo(blue,height,width,i,j);

        } 
    }    
    
    //------- Salvando imagem apos aplicar smooth ------------------------------
    fp1 = fopen(argv[2],"wb+");
    
    for(i=0;i<18;i++)
       fwrite(&vet_antes[i],sizeof(unsigned char),1,fp1);  
       
    fwrite(&width,sizeof(unsigned char),4,fp1);  
    fwrite(&height,sizeof(unsigned char),4,fp1);  
    
    //lendo o cabecalho depois dos valores width e heigth até chegar no primeiro pixel RGB
    for(i=0;i<28;i++)
       fwrite(&vet_depois[i],1,1,fp1);
       
    for(i=0;i<height;i++){
       for(j=0;j<width;j++){
           
           fwrite(&smooth_red[i][j],1,1,fp1);  
           fwrite(&smooth_green[i][j],1,1,fp1);
           fwrite(&smooth_blue[i][j],1,1,fp1);
          
       } 
    }   
    
    free(red);
    free(green);
    free(blue);
    free(smooth_red);
    free(smooth_green);
    free(smooth_blue);
    fclose(fp1);
     
    }// fim do else se o arquivo existir
    fclose(fp);
   
    
    
    tf = clock();
    printf("\nTempo do processamento paralelo: %0.2lf segundos.\n\n",(double)((tf - t0)/CLOCKS_PER_SEC));
    
    return 0;
}

//funcao que calcula a media do grupo 5 x 5
uint32_t soma_grupo(uint32_t **a, int32_t m, int32_t n, int pos_m, int pos_n){
     
     int i,j;
     uint32_t valor = 0;
     
     for(i=pos_m-2; i<=pos_m+2; i++){
     
         for(j=pos_n-2; j<=pos_n+2; j++){
             
             if(i<0 || j<0 || i>=m || j>=n)                   
                    valor += 0;
                          
             else valor += a[i][j];   
                       
         }            
     }
     
     valor = valor / 25;
     
     return valor;
}
